#include <iostream>
#include <string>
#include <vector>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <functional>

// typedef std::function<void()> work_t;
using work_t = std::function<void()>;// 定义函数对象类型
// 包装器 --- 保存任何可调用的对象

enum
{
    OK = 0,
    UsageError,
    PipeError,
    ForkError
};

// 对管道进行： 先描述，再组织
class Channel
{
public:
    Channel(int wfd, pid_t who):_wfd(wfd), _who(who)
    {
        // Channel-3-1234
        _name = "Channel-" + std::to_string(wfd) + std::to_string(who);
    }

    std::string Name() {return _name;};
    ~Channel()
    {
    }

private:
    int _wfd;
    std::string _name;
    pid_t _who;
};

void Usage(std::string proc)
{
    std::cout << "Usage: " << proc << "process-num" << std::endl;
}

void Worker()
{
    // read -> 0 子进程从标准输入上读取数据
    sleep(10);
}

void Print()
{}

void Download()
{}

// channels: 输出型参数
// work_t work : 回调  让子进程执行其他任务
int InitProcessPool(int processnum, std::vector<Channel> &channels, work_t work)
{
        // 2. 创建指定个数个进程
        for (int i = 0; i < processnum; i++)
        {
            // 1. 先有管道
            int pipefd[2] = {0};
            int n = pipe(pipefd);
            if (n < 0)
                return PipeError;
    
            // 2. 创建进程
            pid_t id = fork();
            if (id < 0)
                return ForkError;
    
            // 3. 建立信道
            if (id == 0) // 子进程
            {
                ::close(pipefd[1]); // read
                dup2(pipefd[0], 0);// 重定向到pipefd[0]读端上，让子进程的读取在标准输入中
                Worker();// 子进程进行读取操作
                ::exit(0);
            }
            // 父进程执行
            ::close(pipefd[0]); // wirte
            channels.emplace_back(pipefd[1], id);
            // Channel ch(pipefd[1], id);// 向子进程写入
            // channels.push_back(ch);// 循环num次，就维护了num个管道
            
        }
        return OK;
}

void DebugPrint(std::vector<Channel> &channels)
{
    for(auto &c : channels)
    {
        std::cout << c.Name() << std::endl;
    }
}

// 我们自己就是master
// 父进程 wirte
// 子进程 read
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        return UsageError;
    }
    int num = std::stoi(argv[1]);
    std::vector<Channel> channels;// 管道对象
    InitProcessPool(num, channels, Worker);
    // InitProcessPool(num, channels, Print);
    // InitProcessPool(num, channels, Download);

    DebugPrint(channels);

    sleep(100);

    return 0;
}